package pkmn.battle;

import java.util.List;
import java.util.Random;
import java.util.Scanner;
import pkmn.exception.DeathException;
import pkmn.Pokemon;
import pkmn.experience.ExpList;
import pkmn.status.BattleStatus;
import pkmn.status.BattleStatusEnum;
import pkmn.status.StatusEnum;
import pkmn.util.Util;

public class Battle {

    private BattlePokemon p1;
    private BattlePokemon p2;

    public Battle(Pokemon p1, Pokemon p2) {
        this.p1 = new BattlePokemon(p1);
        this.p2 = new BattlePokemon(p2);
    }

    /**
     * Called before attack
     */
    private void beforeAttack() throws DeathException {
        System.out.println();
        beforeAttack(this.p1);
        beforeAttack(this.p2);
    }

    private void beforeAttack(BattlePokemon battlePokemon) throws DeathException {
        //Check status
        //Paralized
        if (battlePokemon.getPokemon().getStatus().equals(StatusEnum.paralyzis)) {
            if (battlePokemon.getPokemon().getStatus().getTurns() == 0) {
                battlePokemon.getPokemon().removeStatus();
                System.out.println(battlePokemon + " paralyzed no more.");
                battlePokemon.canAttack(true);
            } else if (Util.probability(30.0)) {
                battlePokemon.getPokemon().getStatus().decTurns();
                System.out.println(battlePokemon + " is fully paralyzed!");
                battlePokemon.canAttack(false);
            }
        }
        //Sleeping
        if (battlePokemon.getPokemon().getStatus().equals(StatusEnum.sleep)) {
            if (battlePokemon.getPokemon().getStatus().getTurns() == 0) {
                battlePokemon.getPokemon().removeStatus();
                System.out.println(battlePokemon + " woke up!");
                battlePokemon.canAttack(true);
            } else {
                battlePokemon.getPokemon().getStatus().decTurns();
                System.out.println(battlePokemon + " is fast asleep.");
                battlePokemon.canAttack(false);
            }
        }
        
        //Others status
        List<BattleStatus> listBattleStatus = battlePokemon.getPokemon().getBattleStatus();
        for (BattleStatus bs : listBattleStatus) {
            if (bs.equals(BattleStatusEnum.confusion)) {
                if (bs.getTurns() == 0) {
                    listBattleStatus.remove(bs);
                    System.out.println(battlePokemon + " snapped out of confusion.");
                    battlePokemon.canAttack(true);
                } else {
                    bs.decTurns();
                    System.out.println(battlePokemon + " is confused!");
                    if (Util.probability(50)) {
                        System.out.println("It hurt itself in its confusion!");
                        int damage = battlePokemon.getPokemon().getMaxLife() / 16;
                        battlePokemon.getPokemon().receiveDamage(damage);
                        battlePokemon.canAttack(false);
                    }
                    battlePokemon.canAttack(true);
                }
            }
        }
    }

    /**
     * Called after attack
     * @throws DeathException
     */
    private void afterAttack() throws DeathException {
        afterAttack(this.p1);
        afterAttack(this.p2);
        afterAttack(this.p1, this.p2);
        afterAttack(this.p2, this.p1);
    }

    private void afterAttack(BattlePokemon battlePokemon) throws DeathException {
        //Poisoned
        if (battlePokemon.getPokemon().getStatus().equals(StatusEnum.poison)) {
            System.out.println(battlePokemon + " hurt by poison!");
            battlePokemon.getPokemon().receiveDamage(battlePokemon.getPokemon().getMaxLife() / 16);
        }
        //Burned
        if (battlePokemon.getPokemon().getStatus().equals(StatusEnum.burn)) {
            System.out.println(battlePokemon + " hurt by burn!");
            battlePokemon.getPokemon().receiveDamage(battlePokemon.getPokemon().getMaxLife() / 16);
        }
        //others status

        battlePokemon.canAttack(true);
    }

    private void afterAttack(BattlePokemon battlePokemon1, BattlePokemon battlePokemon2) throws DeathException {
        //BattleStatusEnum
        List<BattleStatus> listBattleStatus = battlePokemon1.getPokemon().getBattleStatus();
        for (BattleStatus bs : listBattleStatus) {
            if (bs.equals(BattleStatusEnum.seed)) {
                int damage = battlePokemon1.getPokemon().getMaxLife() / 16;
                System.out.println("Leech seed saps " + battlePokemon1 + "!");
                battlePokemon1.getPokemon().receiveDamage(damage);
                battlePokemon2.getPokemon().addLife(damage);
                break;
            }
        }

        battlePokemon1.canAttack(true);
        battlePokemon2.canAttack(true);
    }
    
    private void beforeFight(){
        
    }
    
    private void afterFight(){
        int exp = ExpList.getExperience(this.p2.getPokemon());
        if (exp > 0){
            System.out.println(this.p1 + " gained " + exp + " EXP points.");
            this.p1.getPokemon().addExp(exp);
        }
    }

    public void fight() {
        beforeFight();
        try {
            Random random = new Random();
            Scanner scanner = new Scanner(System.in);
            while (true) {
                System.out.println(this.p1.getVisualLife());
                System.out.println(this.p2.getVisualLife());

                //Call battle menu
                System.out.println(this.p1.getMovesMenu());
                int index = scanner.nextInt() - 1;

                //
                //beforeAttack();
                //

                int numberAttacks = 0;
                while (this.p2.getPokemon().getMove()[numberAttacks] != null){
                    numberAttacks++;
                    if (numberAttacks > 3) break;
                }

                if (this.p1.getSpeed() > this.p2.getSpeed()) {
                    beforeAttack(this.p1);
                    if (this.p1.canAttack()) {
                        this.p1.getPokemon().attack(index, this.p2.getPokemon());
                    }
                    beforeAttack(this.p2);
                    if (this.p2.canAttack()) {
                        this.p2.getPokemon().attack(random.nextInt(numberAttacks),
                                this.p1.getPokemon());
                    }
                } else {
                    beforeAttack(this.p2);
                    if (this.p2.canAttack()) {
                        this.p2.getPokemon().attack(random.nextInt(numberAttacks),
                                this.p1.getPokemon());
                    }
                    beforeAttack(this.p1);
                    if (this.p1.canAttack()) {
                        this.p1.getPokemon().attack(index, this.p2.getPokemon());
                    }
                }
                //
                afterAttack();
            }
        } catch (DeathException de) {
            System.out.println(de.getMessage());
        }
        afterFight();
    }
}
